package com.zndroid.utils.impl;

import androidx.annotation.NonNull;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @name:CollectUtil
 * @author:lazy
 * @email:luzhenyuxfcy@sina.com
 * @date : 2020/7/15 23:47
 * @version:
 * @description:集合操作
 * 注意
 * 1.如果泛型 ‘T’ 不是基本类型，需要复写 ‘hashCode()’和‘equals()’方法
 *
 * 2.因为 CopyOnWrite 的写时复制机制，所以在进行写操作的时候，内存里会同时驻扎两个对象的内存，这一点会占用额外的内存空间。
 * 在元素较多或者复杂的情况下，复制的开销很大
 * 复制过程不仅会占用双倍内存，还需要消耗 CPU 等资源，会降低整体性能
 *
 * 3.ArrayList的底层是数组，所以查询的时候直接根据索引可以很快找到对应的元素，
 * 改也是如此，找到index对应元素进行替换。而增加和删除就涉及到数组元素的移动，所以会比较慢.
 *
 * 4.因为每次使用CopyOnWriteArrayList.add都要引起数组拷贝，所以应该避免在循环中使用CopyOnWriteArrayList.add()
 *
 * 5.考虑到List集合对于大数据量操作中比较慢，所以这里转化为Set进行处理，测试发现，在数据量小时二者差别并不大，数据量大时Set就比较有明显优势了
 * 所以这里是采用【空间换时间】的思想处理的，最终选择 Set的形式
 */
@SuppressWarnings("unused")
public class CollectUtil {
    /**
     * 求两个集合的交集
     * @param setA A
     * @param setB B
     * @return Set AnB
     * */
    public <T> Set<T> AnB(@NonNull Set<T> setA, @NonNull Set<T> setB) {
        HashSet<T> sameSet = new HashSet<>();
        for (T t : setA) {
            if(setB.contains(t)){
                sameSet.add(t);
            }
        }
        return sameSet;
    }

    /**
     * 求两个集合的交集
     * @param listA A
     * @param listB B
     * @return List AnB
     * */
    public <T> List<T> AnB(@NonNull List<T> listA, @NonNull List<T> listB) {
        Set<T> setA = new HashSet<>(listA);
        Set<T> setB = new HashSet<>(listB);

        return new CopyOnWriteArrayList<>(AnB(setA, setB));
    }

    /**
     * 去除A集合中B的元素
     * @param setA A
     * @param setB B
     * @return Set A-(AnB)
     * */
    public <T> Set<T> A_AnB(@NonNull Set<T> setA, @NonNull Set<T> setB) {
        setA.removeAll(AnB(setA, setB));
        return setA;
    }

    /**
     * 去除A集合中B的元素
     * @param listA A
     * @param listB B
     * @return Set A-(AnB)
     * */
    public <T> List<T> A_AnB(@NonNull List<T> listA, @NonNull List<T> listB) {
        listA.removeAll(AnB(listA, listB));
        return listA;
    }

    /**
     * 去除B集合中A的元素
     * @param setA A
     * @param setB B
     * @return Set B-(AnB)
     * */
    public <T> Set<T> B_AnB(@NonNull Set<T> setA, @NonNull Set<T> setB) {
        setB.removeAll(AnB(setA, setB));
        return setB;
    }

    /**
     * 去除B集合中A的元素
     * @param listA A
     * @param listB B
     * @return Set B-(AnB)
     * */
    public <T> List<T> B_AnB(@NonNull List<T> listA, @NonNull List<T> listB) {
        listB.removeAll(AnB(listA, listB));
        return listB;
    }

    /**
     * 求两个集合的并集
     * @param setA A
     * @param setB B
     * @return Set AuB
     * */
    public <T> Set<T> AuB(@NonNull Set<T> setA, @NonNull Set<T> setB) {
        HashSet<T> hashSetUnion = new HashSet<>();

        hashSetUnion.addAll(setA);
        hashSetUnion.addAll(setB);

        return hashSetUnion;
    }

    /**
     * 求两个集合的并集
     * @param listA A
     * @param listB B
     * @return List AuB
     * */
    public <T> List<T> AuB(@NonNull List<T> listA, @NonNull List<T> listB) {
        Set<T> setA = new HashSet<>(listA);
        Set<T> setB = new HashSet<>(listB);

        setA.addAll(listA);
        setB.addAll(listB);

        return new CopyOnWriteArrayList<>(AuB(setA, setB));
    }

    /**
     * 两个集合去重
     * @param setA A
     * @param setB B
     * @return Set (AuB)-(AnB)
     * */
    public <T> Set<T> ADupB(@NonNull Set<T> setA, @NonNull Set<T> setB) {
        //(AuB)-(AnB)
        Set<T> AuB = AuB(setA, setB);
        Set<T> AnB = AnB(setA, setB);

        AuB.removeAll(AnB);

        return new HashSet<>(AuB);
    }

    /**
     * 两个集合去重
     * @param listA A
     * @param listB B
     * @return Set (AuB)-(AnB)
     * */
    public <T> List<T> ADupB(@NonNull List<T> listA, @NonNull List<T> listB) {
        Set<T> result;

        HashSet<T> setA = new HashSet<>(listA);
        HashSet<T> setB = new HashSet<>(listB);

        //(AUB)-(AnB)
        Set<T> AuB = AuB(setA, setB);
        Set<T> AnB = AnB(setA, setB);

        AuB.removeAll(AnB);
        result = AuB;

        return new CopyOnWriteArrayList<>(result);
    }

    /**
     * collection is empty or not
     * @param collection collection
     *
     * @return true or false
     * */
    public boolean isEmpty(Collection<?> collection) {
        return null == collection || collection.isEmpty();
    }

    /**
     * map is empty or not
     * @param map map
     * @return true or false
     * */
    public boolean isEmpty(Map<?, ?> map) {
        return null == map || map.isEmpty();
    }

    /**
     * array is empty or not
     * @param array array
     * @return true or false
     * */
    public boolean isEmpty(Object[] array) {
        return null == array || array.length == 0;
    }
}
